package org.xbl.xchain.sdk.crypto.algo;

import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Utils;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.signers.SM2Signer;

import java.security.PrivateKey;
import java.security.PublicKey;

public class SM2 extends ECAlgorithm {

    private SM2() {
        super(AlgorithmType.SM2);
    }

    public static SM2 getInstance() {
        return InnerSingleton.INSTANCE;
    }

    private static class InnerSingleton {
        private static final SM2 INSTANCE = new SM2();
    }

    @Override
    public byte[] sign(PrivateKey privateKey, byte[] msg) throws Exception {
        return sign(privateKey, msg, "SM3withSm2");
    }

    @Override
    public byte[] sign(AsymmetricKeyParameter privateKey, byte[] msg) throws Exception {
        Signer signer = new SM2Signer();
        signer.init(true, privateKey);
        signer.update(msg, 0, msg.length);
        return decodeSignature(signer.generateSignature());
    }

    @Override
    public String genAddressFromPublicKey(String mainPrefix, PublicKey publicKey) throws Exception {
        byte[] hashBytes;
        byte[] bytes = parsePubKey(publicKey);
        hashBytes = hashDigest(bytes, 0, bytes.length, "SM3");
        byte[] md = new byte[20];
        System.arraycopy(hashBytes, 0, md, 0, md.length);
        return org.xbl.xchain.sdk.crypto.encode.Bech32.encode(mainPrefix, encode(0, md));
    }

    public static String genAddressFromPublicKeyBytes(String mainPrefix, byte[] bytes) throws Exception {
        byte[] hashBytes;
        hashBytes = hashDigest(bytes, 0, bytes.length, "SM3");
        byte[] md = new byte[20];
        System.arraycopy(hashBytes, 0, md, 0, md.length);
        return org.xbl.xchain.sdk.crypto.encode.Bech32.encode(mainPrefix, encode(0, md));
    }

    @Override
    public boolean verifySignature(PublicKey publicKey, byte[] msg, byte[] signature) throws Exception {
        return verifySignature(publicKey, msg, signature, "SM3withSM2");
    }

    @Override
    public boolean verifySignature(AsymmetricKeyParameter publicKey, byte[] msg, byte[] signature) throws Exception {
        Signer verifier = new SM2Signer();
        verifier.init(false, publicKey);
        verifier.update(msg, 0, msg.length);
        return verifier.verifySignature(encodeSignature(signature));
    }

    @Override
    byte[] decodeSignature(byte[] derSig) throws Exception {
        ECKey.ECDSASignature signature = ECKey.ECDSASignature.decodeFromDER(derSig);
        byte[] result = new byte[64];
        System.arraycopy(Utils.bigIntegerToBytes(signature.r, 32), 0, result, 0, 32);
        System.arraycopy(Utils.bigIntegerToBytes(signature.s, 32), 0, result, 32, 32);
        return result;
    }
}
